#!/usr/bin/python
import sys

import getopt
import os
import string
import subprocess

#Script format
#easySplit.py [-dvnr] -b #[m,k] [-l #] inputFile outputFolder

#Define expected short and long arguments
_short_args = 'dvrb:a:nh'
_long_args = ['debug', 'verbose', 'readme', 'byte-size', \
    'suffix-length = ', 'dry-run', 'help']

#Define defaults
_script_name = "easySplit"
_version = "1.0"
_debug = 0
_debug_prefix = "*** " + _script_name + " DEBUG: "
_error_prefix = "*** " + _script_name + " ERROR: "
_deb_err_nl_indent = "                    "
_verbose = 0

_byte_size = 0
_byte_size_suffixes = ('m', 'k')


_suffix_length = 2
_dry_run = 0
_output = ''
_input = ''

#Usage information
def usage():
    """Defines the proper usage for this script"""
    print "easySplit.py version " + _version
    print\
    """
    DESCRIPTION:

        Wrapper for the split command that easily allows a user to split a targeted file
        and create a README file that shows how to get the files back together
        without any knowledge of shell scripting.

    USAGE:

        easySplit.py [-dvnr] -b #[m,k] [-l #] inputFile outputFolder

    OPTIONS:
        -d, --debug             Display debug information
        -v, --verbose           Causes the script to be verbal. Off is default.
        -b, --byte-size         The amount of bytes to include in each split file.
                                Adding a 'k' or 'm' alters the value to be in
                                kilobytes and megabytes respectively.
        -a, --suffix-length     Use this many letters to form the suffix portion
                                of the filenames of the split files.  If -a is
                                not specified, the default suffix length
                                will be two.
        -n, --dry-run           NOTE: NOT IMPLEMENTED IN THIS VERSION! Shows you the
                                split command that will be run but does nothing.
        -h, --help              Displays this usage information"""

#Output Debug Info
def debug(sep, first, * moreOutput):
    """Outputs the string given to it with the debug prefix added
       before it if debug is on"""
    if _debug:
        s = ''
        s += _debug_prefix + first
        for o in moreOutput:
            if sep == "\n":
                s += sep + _deb_err_nl_indent + o
            else:
                s += sep + o
        print s

def v(sep, first, * moreOutput):
    """Outputs the string given to it if -v is true"""
    if _verbose:
        s = ''
        s += first
        for o in moreOutput:
            s += sep + o
        print s

def whereis(program):
    for path in os.environ.get('PATH', '').split(':'):
        if os.path.exists(os.path.join(path, program)) and \
            not os.path.isdir(os.path.join(path, program)):
                return os.path.join(path, program)
    return None


def list_to_str(l):
    """Takes a list and returns it as a string"""
    s = ''
    for i in l:
        s = s + ' ' + i
    return s

def do_split(i, o):
    """Splits the input file into the output folder using the split command"""
    v('', "\nSplitting file: ", i)
    v('', "            To: ", o)
    command = "split " + "-b " + str(_byte_size) + " -a " + str(_suffix_length)\
        + " " + i + " " + o

    #Run split
    process = subprocess.Popen(command, bufsize=0, stdin=None, \
                               stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)

    #Wait for split to be finished. Running wait() sets the 'returncode'
    process.wait()

    #What is the return code? Non-zero codes are errors
    if process.returncode != 0:
        err('', "Split had an error: ", process.communicate()[0])
    else:
        v('', "Split complete!")

    #If the user asked for it, create a README text file with the command to
    #join these files back together
    v('', "Creating README")
    t = "The files listed in the directory with this README have been created "\
        + "by the 'split' command. These files were originally:\n\n"\
        + "    " + os.path.split(i)[1] + " (" + str(os.path.getsize(i)) + "b)\n\n"\
        + "To join these split files back into the original, run the "\
        + "following in the directory this README was found in:\n\n"\
        + "    cat " + os.path.split(o)[1] + "* > " + os.path.split(i)[1]\
        + "\n\n Automatic spliting of files and README generation was done by "\
        + "easySplit.py by Collin Brooks 2009 <collin.brooks@gmail.com>"

    debug('', "Absolute output location: ", )
    readme_location = os.path.split(os.path.abspath(o))[0]
    debug('', "Creating readme here: ", readme_location, "/README.txt")
    f = open(readme_location + "/README.txt", "w")
    f.write(t)
    f.close()
    v('', "README created!")

def do_join(i, o):
    """Takes the specifide files in the input folder and outputs them
       to the specified output file"""
    pass

def err(sep, first, * moreOutput):
    """Outputs an error to the screen and then exits"""
    s = ''
    s += _error_prefix + first
    for o in moreOutput:
        if sep == "\n":
            s += sep + _deb_err_nl_indent + o
        else:
            s += sep + o
    print s
    sys.exit(1)

def args_ok(args):
    """Does a preflight check on all of the options and arguments."""
    debug(' ', 'Seeing if the arguments are ok')

    #Need to be able to use the _byte_size as a string

    debug('\n', "Variable information: ", \
          "Suffix Length: " + str(_suffix_length), \
          "Byte Size: " + str(_byte_size))

    #
    #In-depth look at the byte_size option
    #
    if str(_byte_size)[0] not in string.digits:
        debug('', "byte-size doesn't start with a number: byte-size: ", \
              _byte_size, "; byte-size-str: ", _byte_size, "; byte_size_str[0]: ", \
              byte_size_str[0])
        err('', "The byte-size must be a positive number that optionally\
                ends with 'k' or 'm'")
    #We only need to check to see if the suffix is acceptable if the
    #_byte_size is alpha numeric
    elif str(_byte_size).isalnum():
        debug('', "Byte size suffix: ", str(_byte_size)[-1:])
        #Do we have a proper suffix?
        byte_size_suffix_found = str(_byte_size)[-1:] in _byte_size_suffixes
        debug('', "Byte size suffix ok: ", str(byte_size_suffix_found))
        if str(_byte_size)[-1:] not in string.digits and byte_size_suffix_found == False:
            err('', "Invalid byte size value. Leave suffix blank for bytes,\n\
                    add 'k' for kilobytes or add 'm' for megabytes.")
    #
    #Checking out the input and output arguments. Are they bien?
    #
    if len(args) == 0:
        err('The input file and output location could not be determined because\
             the number of arguments after your options is not equal to 2')
    elif args[0][-1:] == '/':
        err('', 'The input file cannot be a directory.')
    #
    #Suffix checks
    #
    elif _suffix_length <= 0:
        err('', 'The suffix length cannot be a negative number')

    #All good, return True
    return True

def main(argv):
    """Takes the arguments from the command line and takes the inputFile and
       splits it in the outputFolder based upon the -byte_size"""
    #Grab globals
    global _debug
    global _verbose
    global _byte_size
    global _suffix_length
    global _dry_run
    global _debug_prefix
    global _input
    global _output
    global _readme

    #First, we need to make sure the split and cat functions can be found
    if whereis('split') == False:
        err('', 'Cannot find the split program')

    #Second, we need to make sure arguments were passed
    if len(argv) == 0:
        debug(' ', "No arguments... showing usage")
        usage()
        sys.exit(2)
    #Grab the passed arguments before the input and output
    try:
        debug(' ', "Passed arguments: " + list_to_str(argv))

        opts, args = getopt.gnu_getopt(argv, _short_args, _long_args)
    except getopt.GetoptError, e:
        #Display usage information and then exit
        err('', str(e))
        usage()
        sys.exit(2)

    #Assign arguments to functions
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
        elif opt == '-d':
            _debug = 1
            _verbose = 1
        elif opt in ("-v", "--verbose"):
            _verbose = 1
        elif opt in ("-b", "byte-size"):
            _byte_size = arg
        elif opt in ("-a", "--suffix-length"):
            _suffix_length = arg
        elif opt in ("-n", "--dry-run"):
            _dry_run = 1
        elif opt in ("-r", "--readme"):
            _readme = True

    #Check that everything is honkey dory with the arguments and options
    if args_ok(args) != True: sys.exit()

    #Try to set input and output locations
    try:
        _input = args[0]
    except InputError("Cannot find the input value"):
        print InputError.__str__

    debug('', "Arguments= ", str(args))
    if(len(args) == 2):
        _input = args[0]
        _output = args[1]
    else:
        err('', "Darn! I can't determine the input or output arguments!")

    do_split(_input, _output)

if __name__ == "__main__":
    main(sys.argv[1:])